#include "wds_enemy.h"
#include "wds_pickup_manager.h"
#include "wds_player.h"
#include "wds_enemy_manager.h"
#include "wds_enemy_collection.h"
#include "wds_bullet.h"
#include "wds_enemy_manager.h"
#include "wds_fade_blood.h"

#include "dd/dd_man.h"
#include "math/facing.h"
#include "math/intersection.h"
#include "num/rand.h"
#include "application/game_script.h"
#include "console/console.h"
#include "ui/ui_manager.h"
#include "gameflow/game_flow_manager.h"
#include "font/font_manager.h"
#include "script/scripthelper.h"
#include "input/keyboard.h"
#include "graphics/graphics_util.h"
#include "luaplus/LuaObject.h"
#include "script/write_lua.h"
#include "lang/lang.h"
#include "vfs/vfs.h"
#include "script/file_list.h"

#include "util/mmgr.h"				// Needs to come in early
#include "util/common_macros.h"		// macros
#include "util/log.h"				// logging
#include "util/timer.h"				// logging



WDSEnemy::WDSEnemy(ENEMY_TYPES EnemyType) : 
										WDSLifeObject(),
										m_EnemyType(EnemyType),															
										m_DelaySpawn(0.0f),										
										m_IsBoss(false),
										m_SpawnPickup(WDSPickup::PICKUP_TYPE_NONE),
										m_EnemyState(ENEMY_STATES_EATING_INIT),																		
										m_Speed(0.05f),										
										m_NextTurnDelay(0),
										m_NextMovementTargetDealy(0),
										m_NextStateDelay(0),
										m_IsLunatic(false),
										m_IsCrawl(false),
										m_EatCurrentLoop(0),
										m_EatMaxLoop(0),
										m_AttackHalfTime(0),
										m_AttackDamage(0.25f),
										m_SpeedScale(1.0)
{
	//m_DamageForColliding = (float)FIGConstants::PLAYER_HP * 0.25f;

	m_TurnSpeed = 150.0f;
	m_Facing.SetTurnSpeed(m_TurnSpeed);
	m_Facing.SetSnap(false);

	const math::Vec2 Pos = math::Vec2( num::RAND32::Instance().RandFloat(0,1.0f),num::RAND32::Instance().RandFloat(0,1.0f));			
	m_Movement.SetCurrent(Pos);
	m_Movement.SetMoveSpeed(m_Speed);	

	float R = num::RAND32::Instance().RandFloat(0,1.0f);
	float G = num::RAND32::Instance().RandFloat(0,1.0f);
	float B = num::RAND32::Instance().RandFloat(0,1.0f);
	m_TestColor = ColorfRGBA(R,G,B, 1.0f);

	m_Life = m_MaxLife = 2.0f;	

	int Rand = num::RAND32::Instance().RandInt(0,5);
	if(Rand == 0)
		m_Prefix = "male1";
	else if(Rand == 1)
		m_Prefix = "male2";
	else if(Rand == 2)
		m_Prefix = "male3";
	else if(Rand == 3)
		m_Prefix = "female1";
	else if(Rand == 4)
		m_Prefix = "female2";
	else if(Rand == 5)
		m_Prefix = "female3";

}


void		WDSEnemy::Render(const math::Vec2& Offset, float Scale)
{
	m_TestOffset = Offset;
	if(m_DelaySpawn > 0)
		return;

	WDSLifeObject::Render(Offset, Scale);
}

void	WDSEnemy::RenderShadow(const math::Vec2& Offset, float Scale)
{
	if(m_DelaySpawn > 0)
		return;

	if(!IsReadyToSpawn() && m_EnemyState != ENEMY_STATES_RISE )
	{
		WDSEnemyManager::Instance().RenderShadowInPos(GetPos(), m_Facing.GetAngle(), Offset, Scale);
	}
}

void		WDSEnemy::Tick(float dt,
						   float GameDifficulty,
						   float PlayerReflex,
						   const math::Vec2& Offset)
{
	
	if(m_DelaySpawn > 0)
	{
		m_DelaySpawn -= dt;
		return;
	}

	if(IsReadyToSpawn())
		return;

	if(m_NextTurnDelay > 0)
		m_NextTurnDelay -= dt;

	if( !IsNearScreen(Offset))
	{
		//m_EnemyState = ENEMY_STATES_IDLE;
	}
	else if( m_EnemyState != ENEMY_STATES_DEAD)
	{
		// if we are in visible area, we need to bump each other
		CheckSurrounding(Offset);
	}
	

	Tick_States(dt, Offset);
	m_Movement.SetMoveSpeed(m_Speed * m_SpeedScale * GameDifficulty * PlayerReflex);
	WDSLifeObject::Tick(dt);		
	


}



void	WDSEnemy::OnDie()
{
	
	WDSLifeObject::OnDie();
	m_Movement.ClearForces();
	m_EnemyState = ENEMY_STATES_DYING_START;	
		
	//if(m_SpawnPickup > WDSPickup::PICKUP_TYPE_NONE)
	if(num::RAND32::Instance().RandInt(0,2) == 0)
	{
		WDSPickupManager::Instance().SpawnChance(m_Movement.GetPos());
	}
	WDSFadeBloodManager::Instance().Spawn( GetPos(), m_Facing.GetAngle(),1.0f);
}


void		WDSEnemy::ReSpawn()
{
	m_Movement.SetMoveSpeed(m_Speed);
	m_Facing.SetTurnSpeed(m_TurnSpeed);
	m_NextMovementTargetDealy = 0;
	WDSLifeObject::ReSpawn();
	m_Movement.ClearForces();
	m_IsLunatic = false;
	m_IsCrawl = false;
	m_EatCurrentLoop = 0;
	m_EatMaxLoop = 0;
}

void		WDSEnemy::InitSpawning(
										float Delay,									
										WDSPickup::PICKUP_TYPES PickupType,
										ENEMY_SPAWN_TYPE SpawnType,
										const math::Vec2& Offset
										)
{
	m_DelaySpawn = Delay;	
	m_SpawnPickup = PickupType;	

	switch(SpawnType)
	{
		case ENEMY_SPAWN_TYPE_OFFSCREEN:
		{
			
			m_EnemyState = ENEMY_STATES_NONE;
			int RandSide = num::RAND32::Instance().RandInt(0,3);
			math::Vec2 Pos;
			if(RandSide == 0)
			{
				Pos.x = -m_Sprite.m_SizeOriginal.x * 0.4f;
				Pos.y =  num::RAND32::Instance().RandFloat(0,1.0f);
			}
			else if(RandSide == 1)
			{
				Pos.x = 1.0f + (m_Sprite.m_SizeOriginal.x * 0.4f);
				Pos.y =  num::RAND32::Instance().RandFloat(0,1.0f);
			}
			else if(RandSide == 2)
			{
				Pos.x =  num::RAND32::Instance().RandFloat(0,1.0f);
				Pos.y = -m_Sprite.m_SizeOriginal.x * 0.4f;
			}
			else if(RandSide == 3)
			{
				Pos.x =  num::RAND32::Instance().RandFloat(0,1.0f);
				Pos.y = 1.0f + (m_Sprite.m_SizeOriginal.x * 0.4f);
			}
			m_Movement.SetCurrent(Pos + Offset);
			
		}
		break;

		case ENEMY_SPAWN_TYPE_GROUND:
			m_EnemyState = ENEMY_STATES_RISE_START;
		break;

		case ENEMY_SPAWN_TYPE_EATING:
			m_EnemyState = ENEMY_STATES_EATING_INIT;
		break;
	}

}

std::string		WDSEnemy::MapTypeToBoneFile(ENEMY_TYPES Type)
{
	switch (Type)
	{
		
		case ENEMY_TYPES_NORMAL:
		break;

		default:
			break;

	}

	return "";
}

std::string		WDSEnemy::MapTypeToName(WDSEnemy::ENEMY_TYPES Type)
{
	switch (Type)
	{
		case ENEMY_TYPES_NORMAL:
		return "normal";

		

		default:
			break;

	}

	return "unknown";
}

WDSEnemy::ENEMY_TYPES		WDSEnemy::MapNameToType(const std::string& TypeName)
{
	for (unsigned int i = 0; i < ENEMY_TYPES_COUNT; i++)
		if (strcmpi( MapTypeToName((ENEMY_TYPES)i).c_str(), TypeName.c_str() ) == 0)
			return (ENEMY_TYPES)i;

	return ENEMY_TYPES_NONE;  	
}



void	WDSEnemy::Turn(float Degree)
{
	if(m_NextTurnDelay > 0)
		return;

	m_NextTurnDelay = 0.01f;
	math::Vec2 TempDir = math::Facing::VectorFromAngle(Degree);
	TempDir.y /= GraphicsUtil::W2H;
	m_Facing.SetTarget(math::Facing::AngleVector(TempDir));

}



void		WDSEnemy::AvoidMovement(float Degree)
{
	/*
	float DegreeToPlayer = Degree;
	WDSPlayer* pPlayer = WDSEnemyManager::Instance().GetPlayer();
	if(pPlayer)
	{
		const math::Vec2 Dir = pPlayer->GetPos() - GetPos();
		DegreeToPlayer = math::Facing::AngleVector( Dir);
	}

	float Diff = DegreeToPlayer - Degree;
	if(Diff < 0)
		Diff *= -1;

	if(Diff > 135.0f )
	{
		// he is going almost opposite site of the player
		int debug = 1;
	}
	*/

	if( !m_IsLunatic && !m_IsCrawl &&  m_CurrentAnimation.compare("walk") != 0)
	{
		SetAnimation("walk");
		//SetAnimation("chase");
	}
	this->Turn(Degree);		
	m_Movement.SetTarget(Degree);
	m_NextMovementTargetDealy = num::RAND32::Instance().RandFloat(0.25f ,1.5f);

	
}


void		WDSEnemy::CheckSurrounding(const math::Vec2& Offset)
{
	std::vector<WDSEnemyCollection*> EnemyCollection;
	WDSEnemyManager::Instance().GetEnemyCollection(EnemyCollection);

	m_Neibhours.resize(0);
	//---------------- looking for Neibhours -----------------------------
	for(int i = 0; i < (int)EnemyCollection.size(); i++)
	{
		WDSEnemyCollection* pEnemyCollection = EnemyCollection[i];

		for(int j = 0; j < pEnemyCollection->GetCount(); j++)
		{
			WDSEnemy* pEnemy = pEnemyCollection->GetEnemyByIndex(j);

			if(!pEnemy->IsAlive())	// active? alive?
				continue;

			if(!pEnemy->IsNearScreen(Offset))	// visible?
				continue;

			if(pEnemy == this)	// myself 
				continue;

			const float Dist = (pEnemy->GetPos() - GetPos()).Magnitude();			
			if( Dist < (m_Sprite.m_SizeOriginal.x + pEnemy->m_Sprite.m_SizeOriginal.x) * 1.5f )
				m_Neibhours.push_back(pEnemy);

			// bump here
			if( Dist < m_Sprite.m_SizeOriginal.x * 0.5f )
			{
				
				math::Vec2 Dir = pEnemy->GetPos() - GetPos();	
				const math::Vec2 CurrentDir =  m_Movement.GetTarget();

				//don't bump too much where I am going otherwise, it may look like movespeed is too much
				if( CurrentDir.x > 0 && Dir.x > 0 && Dir.x > CurrentDir.x)
					Dir.x = CurrentDir.x;
				else if( CurrentDir.x < 0 && Dir.x < 0 && Dir.x < CurrentDir.x)
					Dir.x = CurrentDir.x;
				if( CurrentDir.y > 0 && Dir.y > 0 && Dir.y > CurrentDir.y)
					Dir.y = CurrentDir.y;
				else if( CurrentDir.y < 0 && Dir.y < 0 && Dir.y < CurrentDir.y)
					Dir.y = CurrentDir.y;

				m_Movement.AddForce(Dir * -1.0f,1.0f,0.25f);
				
			}

		}
	}
	//---------------- End looking for Neibhours -----------------------------
	
	

}

bool	WDSEnemy::IsNearScreen(const math::Vec2& Offset)
{
	math::Vec2 ScreenPos = GetPos() - Offset;
	if( ScreenPos.x < -0.1f)
		return false;
	
	if(ScreenPos.x > 1.1f)
		return false;

	if(ScreenPos.y < -0.1f)
		return false;
	
	if(ScreenPos.y > 1.1f)
		return false;

	return true;
}

bool	WDSEnemy::HitTest(WDSBullet* pBullet)
{
	if(!m_IsAlive)
		return false;

	if(m_EnemyState == ENEMY_STATES_RISE_START || m_EnemyState == ENEMY_STATES_RISE  )
		return false;

	if(pBullet)
	{
		if(pBullet->GetIgnoreObj() == this)
			return false;
	}

	bool IsHit = math::Intersection::CapsuleCapsule2D(	pBullet->GetPreviousPos(), 
														pBullet->GetPos(), 
														m_Movement.GetPreviousPos(),
														GetPos(),
														pBullet->GetRadius(), 
														m_Sprite.m_SizeOriginal.x * 0.5f);
	if(IsHit)
	{
		if(pBullet)
		{
			GotDamaged(pBullet->GetDamagePower(),pBullet,false);
			
			if(pBullet->GetType() == WDSBullet::BULLET_TYPE_PULSE)
			{
				// push back
				const math::Vec2 Dir = ((pBullet->GetPreviousPos() - GetPos()) * -1.0f);
				m_Movement.AddForce(Dir,0.5f,1.5f);
				
			}
			pBullet->OnHit(this);

			if(m_Life > 0)
			{
				m_EnemyState = ENEMY_STATES_GETHIT_START;				
			}
			
		}
		
	}
	return IsHit; 
}

bool	WDSEnemy::IsOtherEnemyInThatDegree(float Degree, float Dist, bool TestDraw)
{
	math::Vec2 Intersection;	
	math::Vec2 To = math::Facing::VectorFromAngle(Degree);
	//To.y /= GraphicsUtil::W2H;
	To.Normalize();
	To *= Dist;
	To.y *= GraphicsUtil::W2H;
	To += GetPos();
	
	for(int i =0; i < (int)m_Neibhours.size(); i++)
	{
		if(!m_Neibhours[i]->IsAlive())
			continue;

		if( math::Intersection::RayCircle(GetPos() , To , m_Neibhours[i]->GetPos(), m_Neibhours[i]->m_Sprite.m_SizeOriginal.x * 0.5f , Intersection) )		
		{
			
			//if(TestDraw)
			{
				//dd::Manager&	DD = dd::Manager::Instance();
				//DD.AddLine2D( GetPos() - m_TestOffset , To - m_TestOffset , m_TestColor );

				//DD.AddCircle2D( m_Neibhours[i]->GetPos() - m_TestOffset , m_Neibhours[i]->m_Size.x * 0.5f , m_TestColor );
			}

			return true;
		}
		//dd::Manager&	DD = dd::Manager::Instance();
		//DD.AddLine2D( GetPos() - m_TestOffset , To - m_TestOffset , m_TestColor );

		//DD.AddCircle2D( m_Neibhours[i]->GetPos() - m_TestOffset , m_Neibhours[i]->m_Size.x * 0.5f , m_TestColor );
	}
	
	return false;
}

void	WDSEnemy::SetAnimation(const std::string& Name)
{
	char buffer[512];
	sprintf(buffer, "%s_%s", m_Prefix.c_str() , Name.c_str());
	m_Sprite.Create(buffer); 	
	m_CurrentAnimation = Name;
}

void	WDSEnemy::CheckAttackHit(float Scale)
{
	
	float AttackLength = m_Sprite.m_Size.x * 0.8f;
	WDSPlayer* pPlayer = WDSEnemyManager::Instance().GetPlayer();
	if(pPlayer)
	{
		math::Vec2 Dir = pPlayer->GetPos() - GetPos();
		Dir.y *= GraphicsUtil::H2W;
		if ( Dir.Magnitude() <  AttackLength )
		{
			pPlayer->GotDamaged(m_AttackDamage, NULL, false);
		}
	}		
} 

